//  Copyright (c) Microsoft Corporation.  All Rights Reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Security.Principal;

using System.IdentityModel.Claims;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;

namespace EAI.SAML
{
    public class EAISamlSubject
    {
        // The below property has not been added.
        // <BaseID>, <NameID>, or <EncryptedID> [Optional]
        // Identifies the subject.
        readonly ImmutableCollection<EAISamlSubjectConfirmation> subjectConfirmations = new ImmutableCollection<EAISamlSubjectConfirmation>();

        bool isReadOnly = false;
        IIdentity primaryIdentity;
        List<ClaimSet> subjectKeyClaimSet = null;

        public EAISamlSubject()
        {
        }

        public EAISamlSubject(XmlReader reader, EAISamlSerializer serializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)
        {
            this.ReadXml(reader, serializer, keyInfoSerializer, outOfBandTokenResolver);
        }

        public IList<EAISamlSubjectConfirmation> SubjectConfirmations
        {
            get { return this.subjectConfirmations; }
        }

        public virtual List<ClaimSet> ExtractSubjectKeyClaimSet(EAISamlAssertion assertion, EAISamlSecurityTokenAuthenticator authenticator)
        {
            if (this.subjectKeyClaimSet == null)
            {
                this.subjectKeyClaimSet = new List<ClaimSet>();
                foreach (EAISamlSubjectConfirmation confirmation in subjectConfirmations)
                {
                    if (confirmation.SubjectConfirmationData != null)
                    {
                        foreach (SecurityKeyIdentifier keyIdentifier in confirmation.SubjectConfirmationData.KeyInfos)
                        {
                            ClaimSet authClaimset = authenticator.ResolveClaimSet(assertion, keyIdentifier);
                            if (authClaimset != null)
                                this.subjectKeyClaimSet.Add(authClaimset);
                        }
                    }
                }

                Claim identityClaim = null;
                foreach (ClaimSet claimSet in this.subjectKeyClaimSet)
                {
                    foreach (Claim claim in claimSet.FindClaims(null, Rights.Identity))
                    {
                        identityClaim = claim;
                        break;
                    }

                    if (identityClaim != null)
                        break;
                }

                if (identityClaim != null)
                {
                    this.primaryIdentity = new GenericIdentity(identityClaim.Resource.ToString(), this.GetType().Name);
                }
                else
                {
                    this.primaryIdentity = null;
                }
            }

            return subjectKeyClaimSet;
        }

        internal IIdentity PrimaryIdentity
        {
            get { return this.primaryIdentity; }
        }

        public void MakeReadOnly()
        {
            if (!this.isReadOnly)
            {
                for (int i = 0; i < this.subjectConfirmations.Count; ++i)
                {
                    this.subjectConfirmations[i].MakeReadOnly();
                }
                this.subjectConfirmations.MakeReadOnly();

                this.isReadOnly = true;
            }
        }

        public virtual void ReadXml(XmlReader reader, EAISamlSerializer serializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            if (serializer == null)
                throw new ArgumentNullException("serializer");

            reader.ReadStartElement(EAISamlConstants.Subject, EAISamlConstants.Namespace);
            if (reader.IsEmptyElement)
            {
                reader.MoveToContent();
                reader.Read();
            }

            while (reader.IsStartElement(EAISamlConstants.SubjectConfirmation, EAISamlConstants.Namespace))
            {
                EAISamlSubjectConfirmation subjectConfirmation = serializer.LoadSubjectConfirmation(reader, keyInfoSerializer, outOfBandTokenResolver);
                if (subjectConfirmation != null)
                    this.subjectConfirmations.Add(subjectConfirmation);
            }

            reader.ReadEndElement();
        }

        public virtual void WriteXml(XmlWriter writer, SecurityTokenSerializer tokenSerializer)
        {
            if (writer == null)
                throw new ArgumentNullException("writer");

            writer.WriteStartElement(EAISamlConstants.Subject, EAISamlConstants.Namespace);

            for (int i = 0; i < this.subjectConfirmations.Count; ++i)
                this.subjectConfirmations[i].WriteXml(writer, tokenSerializer);

            writer.WriteEndElement();
        }

    }

}
